#include <algorithm>

#include <boost/any.hpp>
#include <boost/foreach.hpp>
#include <boost/date_time.hpp>
#include <boost/algorithm/string.hpp>

#include <Wt/WPen>
#include <Wt/WRectF>
#include <Wt/WBrush>
#include <Wt/WPointF>
#include <Wt/WString>
#include <Wt/WPainter>
#include <Wt/WDateTime>
#include <Wt/WRectArea>
#include <Wt/Chart/WAxis>
#include <Wt/WPainterPath>
#include <Wt/WApplication>
#include <Wt/WPaintDevice>
#include <Wt/WAbstractItemModel>

#include "genetic_lego/LegoRenderer.h"
#include "genetic_lego/LegoDescription.h"

using namespace Wt;
using namespace std;
#define foreach         BOOST_FOREACH
#define reverse_foreach BOOST_REVERSE_FOREACH


LegoRenderer::LegoRenderer( Wt::WContainerWidget *parent )
  : WPaintedWidget( parent )
{
//  setPreferredMethod( WPaintedWidget::InlineSvgVml );

}//LegoRenderer constructor

LegoRenderer::~LegoRenderer()
{
}


void LegoRenderer::paintEvent( Wt::WPaintDevice *paintDevice )
{
    Wt::WPainter painter(paintDevice);
    paintLegoScene( paintDevice, m_scene );
}//paintEvent(...)


void LegoRenderer::setScene( boost::shared_ptr<const LegoSceneDescription> s )
{
  m_scene = s;
  update(); // trigger a repaint
}//void setScene( boost::shared_ptr<const LegoSceneDescription> )


int nint( double f ){ return static_cast<int>( floor(f+0.5) ); }

#include <cv.h>
#include <highgui.h>
void LegoRenderer::paintLegoScene( IplImage *img,
                                   boost::shared_ptr<const LegoSceneDescription> scene )
{
  if( !img || !scene )
    return;

  const double frac = 0.25;
  const double pix_per_cell = static_cast<double>( scene->pixels_per_cell );

  const vector<LegoDescription> &legos = scene->legos;
  const size_t nlegos = legos.size();

  //Lets draw the background using the color of og the very first lego
  {//begin codeblock to paint background
    const int width = img->width;
    const int height = img->height;

    const LegoDescription &lego = legos[0];

    WColor penColor = lego.penColor();
    WColor fillColor = lego.fillColor();


    CvScalar pen = cvScalar( penColor.blue(), penColor.green(), penColor.red() );
    CvScalar brush = cvScalar( fillColor.blue(), fillColor.green(), fillColor.red() );

    cvRectangle( img, cvPoint(0,0), cvPoint(width,height), brush, -1 );
    cvRectangle( img, cvPoint(0,0), cvPoint(width,height), pen, 1 );

    const int ncellswide = 1 + width / scene->pixels_per_cell;
    const int ncellstall = 1 + height / scene->pixels_per_cell;

    for( int xcell = 0; xcell < ncellswide; ++xcell )
    {
      const double x = (xcell-0.5) * pix_per_cell;
      const double ul_x = x + frac*pix_per_cell;
      const double lr_x = x + (1.0-frac)*pix_per_cell;

      for( int ycell = 0; ycell < ncellstall; ++ycell )
      {
        const double y = (ycell-0.5) * pix_per_cell;
        const double ul_y = y + frac*pix_per_cell;
        const double lr_y = y + (1.0-frac)*pix_per_cell;

        const int rad = nint( 0.25*scene->pixels_per_cell*(1.0 - frac) );
        CvPoint center = cvPoint( nint(0.5*(ul_x+lr_x)), nint(0.5*(ul_y+lr_y)) );

//        cvCircle( img, center, rad, brush, -1);
        cvCircle( img, center, rad, pen, 1 );
      }//for( int ycell = 0; ycell < ncellstall; ++ycell )
    }//for( int xcell = 0; xcell < ncellswide; ++xcell )
  }//end codeblock to paint background


  //  foreach( const LegoDescription &lego, legos )
  for( size_t i = 1; i < nlegos; ++i )
  {
    const LegoDescription &lego = legos[i];

    const double x = scene->xStartPosInPixels( lego );
    const double y = scene->yStartPosInPixels( lego );
    const double w = scene->legoWidthInPixels( lego );
    const double h = scene->legoHeightInPixels( lego );

    WColor penColor = lego.penColor();
    WColor fillColor = lego.fillColor();
    penColor.setRgb( 0, 0, 0 );

    CvScalar pen = cvScalar( penColor.blue(), penColor.green(), penColor.red() );
    CvScalar brush = cvScalar( fillColor.blue(), fillColor.green(), fillColor.red() );

    cvRectangle( img, cvPoint(x,y), cvPoint(x+w,y+h), brush, -1 );
    cvRectangle( img, cvPoint(x,y), cvPoint(x+w,y+h), pen, 1 );

#if(DRAW_CIRCLES)
    const int nrow = lego.nrows();
    const int ncol = lego.ncolomuns();

    for( int row = 0; row < nrow; ++row )
    {
      const double ul_y = y + row*pix_per_cell + frac*pix_per_cell;
      const double lr_y = y + row*pix_per_cell + (1.0-frac)*pix_per_cell;

      for( int col = 0; col < ncol; ++col )
      {
        const double ul_x = x + col*pix_per_cell + frac*pix_per_cell;
        const double lr_x = x + col*pix_per_cell + (1.0-frac)*pix_per_cell;

        const int rad = nint( 0.25*scene->pixels_per_cell*(1.0 - frac) );
        CvPoint center = cvPoint( nint(0.5*(ul_x+lr_x)), nint(0.5*(ul_y+lr_y)) );
//        cvCircle( img, center, rad, brush, -1);
        cvCircle( img, center, rad, pen, 1 );
      }//for( int col = 0; col < ncol; ++col )
    }//for( int row = 0; row < nrow; ++row )
#endif
  }//foreach( const LegoDescription &lego, legos )
}//void LegoRenderer::paintLegoScene( IplImage *, ... )



void LegoRenderer::paintLegoScene( Wt::WPaintDevice *paintDevice,
                       boost::shared_ptr<const LegoSceneDescription> scene )
{
  if( !scene || !paintDevice )
    return;

  const double frac = 0.25;
  const double pix_per_cell = static_cast<double>( scene->pixels_per_cell );

  Wt::WPainter painter( paintDevice );

  const vector<LegoDescription> &legos = scene->legos;

  const size_t nlegos = legos.size();

  //Lets draw the background using the color of og the very first lego
  {//begin codeblock to paint background
    const int width = paintDevice->width().toPixels();
    const int height = paintDevice->height().toPixels();

    const LegoDescription &lego = legos[0];
    WPen pen( lego.penColor() );
    WBrush brush( lego.fillColor() );
    painter.setPen( pen );
    painter.setBrush( brush );
    painter.drawRect( 0, 0, width, height );

#if(DRAW_CIRCLES)
    const int ncellswide = 1 + width / scene->pixels_per_cell;
    const int ncellstall = 1 + height / scene->pixels_per_cell;

    for( int xcell = 0; xcell < ncellswide; ++xcell )
    {
      const double x = (xcell-0.5) * pix_per_cell;
      const double ul_x = x + frac*pix_per_cell;
      const double lr_x = x + (1.0-frac)*pix_per_cell;

      for( int ycell = 0; ycell < ncellstall; ++ycell )
      {
        const double y = (ycell-0.5) * pix_per_cell;
        const double ul_y = y + frac*pix_per_cell;
        const double lr_y = y + (1.0-frac)*pix_per_cell;

        WPointF topleft( ul_x, ul_y );
        WPointF bottomright( lr_x, lr_y );

        WRectF rect( topleft, bottomright );
        pen.setColor( Wt::black );
        painter.setPen( pen );
        painter.drawArc( rect, 0.0, 360.0*16.0 );
      }//for( int ycell = 0; ycell < ncellstall; ++ycell )
    }//for( int xcell = 0; xcell < ncellswide; ++xcell )
#endif
  }//end codeblock to paint background


  //  foreach( const LegoDescription &lego, legos )
  for( size_t i = 1; i < nlegos; ++i )
  {
    const LegoDescription &lego = legos[i];

    const double x = scene->xStartPosInPixels( lego );
    const double y = scene->yStartPosInPixels( lego );
    const double w = scene->legoWidthInPixels( lego );
    const double h = scene->legoHeightInPixels( lego );

    WPen pen( lego.penColor() );
    WBrush brush( lego.fillColor() );

    pen.setColor( Wt::black );
    pen.setStyle( Wt::SolidLine );

    painter.setPen( pen );
    painter.setBrush( brush );
    painter.drawRect( x, y, w, h );

#if(DRAW_CIRCLES)
    const int nrow = lego.nrows();
    const int ncol = lego.ncolomuns();

    for( int row = 0; row < nrow; ++row )
    {
      const double ul_y = y + row*pix_per_cell + frac*pix_per_cell;
      const double lr_y = y + row*pix_per_cell + (1.0-frac)*pix_per_cell;

      for( int col = 0; col < ncol; ++col )
      {
        const double ul_x = x + col*pix_per_cell + frac*pix_per_cell;
        const double lr_x = x + col*pix_per_cell + (1.0-frac)*pix_per_cell;

        WPointF topleft( ul_x, ul_y );
        WPointF bottomright( lr_x, lr_y );

        WRectF rect( topleft, bottomright );
        pen.setColor( Wt::black );
        painter.setPen( pen );
        painter.drawArc( rect, 0.0, 360.0*16.0 );
      }//for( int col = 0; col < ncol; ++col )
    }//for( int row = 0; row < nrow; ++row )
#endif
  }//foreach( const LegoDescription &lego, legos )
}//paintLegoScene(...)



